#ifndef COOLPOOL_SIMULATION_SIMULATION_H_
#define COOLPOOL_SIMULATION_SIMULATION_H_

#include <iostream>
#include <stdint.h>
#include <vector>
#include <Vector3D.h>
#include <boost/thread.hpp>

#include "CollisionDetectionWorkCollector.h"
#include "DetectionCheckWorker.h"
#include "CollisionHandler.h"
#include "CollisionTimeFinder.h"
#include "EigenTools.h"
#include "Entity.h"
#include "EntitiesContainer.h"
#include "DynamicEntity.h"
#include "RestingContactHandler.h"
#include "TimeMeasure.h"
#include "WorldGrid.h"

namespace coolpool
{
	// Abstract base class for all simulations, for use it needs:
	// - CollisionTimeFinder
	// - CollisionHandler
	// - CollisionRestingContactHandler
	// - List of entities
	// - num_workers - how many parallel workers will the simulation using
	//
	// It is designed to be runned in a different thread, the starting point for this thread
	// is run() method. It has its basic implementation but may be changed if necessary.
	//
	// The methods to acces elements are not synchronized, so before calling for example,
	// getRenderables() it is necessary to lock the object.
	class Simulation
	{
	public:

		virtual ~Simulation();

		// Collects the renderable objects from all entities.
		// Before calling it necessary to lock the simulation, to get valid renderable objects.
		std::vector<IRenderable *> * getRenderables();

		// Starting point for the thread.
		virtual void run();

		// Method called on the beginning of each step, for example here may be the forces acting on the body updated.
		virtual void beforeNextStep() = 0;

		void kill();
		inline EntitiesContainer * getEntitiesContainer()
		{
			return entities_container_;
		}
		// Synchronization entities:
		inline boost::condition_variable * getCondition()
		{
			return &cond_;
		}
		inline boost::condition_variable * getWorkCollectorCondition()
		{
			return collision_work_collector.getCond();
		}
		inline boost::mutex * getLock()
		{
			return &lock_;
		}
		inline bool isKilled()
		{
			return kill_;
		}

	protected:

		Simulation(const std::vector<Entity *> & entities, CollisionTimeFinder * collision_time_finder, CollisionHandler * collision_handler, RestingContactHandler * resting_contact_handler,
				int num_workers);

		// Puts the given collision to the right vector and removes the unnecessary collisions from the stack.
		// It is necessary to delete the collisions in firsts, resting_contact after use.
		void enqueueCollision(const Collision * col, std::vector<const Collision *> * firsts, std::vector<const Collision *> * resting_contact);

		// Updates the vectors of collisions which need to be resolved.
		// firsts - collisions which will occur as first in the given timestep.
		// resting_contact - collisions which will be handled as resting contact.
		// time_step - time step during which collisions are searched.
		void findCollisions(std::vector<const Collision *> * firsts, std::vector<const Collision *> * resting_contact, double time_step);

		// Moves the simulation to the next time frame.
		void nextStep(double time_step);

	private:

		// Synchronization entity for the workers.
		std::vector<int> done;

		// Handler for collisions between the given set of entities.
		CollisionHandler * collision_handler_;

		// CollisionTimeFinder for given set of entities.
		CollisionTimeFinder * collision_time_finder_;

		// Collector of detection pairs for each frame, it is used as an synchronizing
		// element for workers.
		CollisionDetectionWorkCollector collision_work_collector;

		// Condition variable for synchronizating rendering and simulation.
		boost::condition_variable cond_;

		// Detection check workers.
		std::vector<DetectionCheckWorker *> detection_check_workers;

		// Container of all entities.
		EntitiesContainer * entities_container_;

		// Main loop executor.
		bool kill_;

		// Lock on the whole object.
		boost::mutex lock_;

		// Number of workers.
		int num_workers_;

		RestingContactHandler * resting_contact_handler_;

		// Time step of the simulation.
		double time_step;

		// Threads.
		std::vector<boost::thread *> worker_threads;

		WorldGrid * world_grid_;

		void init();
	};
}
#endif
